/*
 * Copyright (c) JForum Team
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, 
 * with or without modification, are permitted provided 
 * that the following conditions are met:
 * 
 * 1) Redistributions of source code must retain the above 
 * copyright notice, this list of conditions and the 
 * following  disclaimer.
 * 2)  Redistributions in binary form must reproduce the 
 * above copyright notice, this list of conditions and 
 * the following disclaimer in the documentation and/or 
 * other materials provided with the distribution.
 * 3) Neither the name of "Rafael Steil" nor 
 * the names of its contributors may be used to endorse 
 * or promote products derived from this software without 
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT 
 * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
 * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
 * IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
 * 
 * This file creation date: 29/01/2006 - 12:19:11
 * The JForum Project
 * http://www.jforum.net
 */
package net.jforum;

import java.io.IOException;
import java.sql.Connection;

import javax.servlet.http.HttpServletResponse;

import net.jforum.context.ForumContext;
import net.jforum.context.RequestContext;
import net.jforum.context.ResponseContext;
import net.jforum.exceptions.ForumException;
import net.jforum.util.preferences.ConfigKeys;
import net.jforum.util.preferences.SystemGlobals;

import org.apache.log4j.Logger;

import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.SimpleHash;

/**
 * Data execution context.
 * 
 * @author Rafael Steil
 * @version $Id: JForumExecutionContext.java,v 1.10 2006/10/10 01:59:55
 *          rafaelsteil Exp $
 */
public class JForumExecutionContext {
    private static ThreadLocal userData = new ThreadLocal();
    private static Logger logger = Logger
	    .getLogger(JForumExecutionContext.class);
    private static Configuration templateConfig;

    private Connection conn;
    private ForumContext forumContext;
    private SimpleHash context = new SimpleHash(ObjectWrapper.BEANS_WRAPPER);
    private String redirectTo;
    private String contentType;
    private boolean isCustomContent;
    private boolean enableRollback;

    /**
     * Gets the execution context.
     * 
     * @return JForumExecutionContext
     */
    public static JForumExecutionContext get() {
	JForumExecutionContext ex = (JForumExecutionContext) userData.get();

	if (ex == null) {
	    ex = new JForumExecutionContext();
	    userData.set(ex);
	}

	return ex;
    }

    /**
     * Checks if there is an execution context already set
     * 
     * @return <code>true</code> if there is an execution context
     * @see #get()
     */
    public static boolean exists() {
	return (userData.get() != null);
    }

    /**
     * Sets the default template configuration
     * 
     * @param config
     *            The template configuration to set
     */
    public static void setTemplateConfig(Configuration config) {
	templateConfig = config;
    }

    /**
     * Gets a reference to the default template configuration settings.
     * 
     * @return The template configuration instance
     */
    public static Configuration templateConfig() {
	return templateConfig;
    }

    /**
     * Sets the execution context
     * 
     * @param ex
     *            JForumExecutionContext
     */
    public static void set(JForumExecutionContext ex) {
	userData.set(ex);
    }

    /**
     * Sets a connection
     * 
     * @param conn
     *            The connection to use
     */
    public void setConnection(Connection conn) {
	this.conn = conn;
    }

    /**
     * Gets the current thread's connection
     * 
     * @return Connection
     */
    public static Connection getConnection() {
	return getConnection(true);
    }

    public static Connection getConnection(boolean validate) {
	JForumExecutionContext ex = get();
	Connection c = ex.conn;

	if (validate && c == null) {
	    c = DBConnection.getImplementation().getConnection();

	    try {
		c.setAutoCommit(!SystemGlobals
			.getBoolValue(ConfigKeys.DATABASE_USE_TRANSACTIONS));
	    } catch (Exception e) {
		// catch error autocommit
	    }

	    ex.setConnection(c);
	    set(ex);
	}

	return c;
    }

    public static ForumContext getForumContext() {
	return ((JForumExecutionContext) userData.get()).forumContext;
    }

    public void setForumContext(ForumContext forumContext) {
	this.forumContext = forumContext;
    }

    /**
     * Gets the current thread's request
     * 
     * @return WebContextRequest
     */
    public static RequestContext getRequest() {
	return getForumContext().getRequest();
    }

    /**
     * Gets the current thread's response
     * 
     * @return HttpServletResponse
     */
    public static ResponseContext getResponse() {
	return getForumContext().getResponse();
    }

    /**
     * Gets the current thread's template context
     * 
     * @return SimpleHash
     */
    public static SimpleHash getTemplateContext() {
	return ((JForumExecutionContext) userData.get()).context;
    }

    /**
     * Gets the current thread's <code>DataHolder</code> instance
     * 
     * @param redirect
     *            String
     */
    public static void setRedirect(String redirect) {
	((JForumExecutionContext) userData.get()).redirectTo = redirect;
    }

    /**
     * Sets the content type for the current http response.
     * 
     * @param contentType
     *            String
     */
    public static void setContentType(String contentType) {
	((JForumExecutionContext) userData.get()).contentType = contentType;
    }

    /**
     * Gets the content type for the current request.
     * 
     * @return String
     */
    public static String getContentType() {
	return ((JForumExecutionContext) userData.get()).contentType;
    }

    /**
     * Gets the URL to redirect to, if any.
     * 
     * @return The URL to redirect, of <code>null</code> if none.
     */
    public static String getRedirectTo() {
	JForumExecutionContext ex = (JForumExecutionContext) userData.get();
	return (ex != null ? ex.redirectTo : null);
    }

    /**
     * Marks the request to use a binary content-type.
     * 
     * @param enable
     *            boolean
     */
    public static void enableCustomContent(boolean enable) {
	((JForumExecutionContext) userData.get()).isCustomContent = enable;
    }

    /**
     * Checks if the current request is binary
     * 
     * @return <code>true</code> if the content tyee for the current request is
     *         any binary data.
     */
    public static boolean isCustomContent() {
	return ((JForumExecutionContext) userData.get()).isCustomContent;
    }

    /**
     * Forces the request to not commit the connection.
     */
    public static void enableRollback() {
	((JForumExecutionContext) userData.get()).enableRollback = true;
    }

    /**
     * Check if commit is disabled or not for the current request.
     * 
     * @return <code>true</code> if a commit should NOT be made
     */
    public static boolean shouldRollback() {
	return ((JForumExecutionContext) userData.get()).enableRollback;
    }

    /**
     * Send UNAUTHORIZED to the browser and ask user to login via basic
     * authentication
     */
    public static void requestBasicAuthentication() {
	getResponse().addHeader("WWW-Authenticate", "Basic realm=\"JForum\"");

	try {
	    getResponse().sendError(HttpServletResponse.SC_UNAUTHORIZED);
	} catch (IOException e) {
	    throw new ForumException(e);
	}

	enableCustomContent(true);
    }

    /**
     * Finishes the execution context
     */
    public static void finish() {
	Connection conn = JForumExecutionContext.getConnection(false);

	if (conn != null) {
	    if (SystemGlobals
		    .getBoolValue(ConfigKeys.DATABASE_USE_TRANSACTIONS)) {
		if (JForumExecutionContext.shouldRollback()) {
		    try {
			conn.rollback();
		    } catch (Exception e) {
			logger.error("Error while rolling back a transaction",
				e);
		    }
		} else {
		    try {
			conn.commit();
		    } catch (Exception e) {
			logger.error("Error while commiting a transaction", e);
		    }
		}
	    }

	    try {
		DBConnection.getImplementation().releaseConnection(conn);
	    } catch (Exception e) {
		logger.error("Error while releasing the connection : " + e, e);
	    }
	}

	userData.set(null);
    }
}
